home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1996 January / macformat-033.iso / mac / Shareware City / Developers / VideoToolbox / VideoToolboxSources / GDTestClut.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-26  |  19.0 KB  |  590 lines  |  [TEXT/CWIE]

  1. /*
  2. GDTestClut.c
  3.  
  4. This is used by TimeVideo; I can’t think of any reason for including it in any
  5. other program.
  6.  
  7. OSErr GDTestClut(FILE *o[2],GDHandle device,short flags,VideoInfo *card);
  8.  
  9. GDTestClut.c tests whether the video clut can be written and read faithfully,
  10. and saves the results in a user-supplied VideoInfo structure. The test consists
  11. of writing random numbers to all the clut entries, reading them back, and
  12. comparing. I’ve been surprised to find more than a few video drivers that fail
  13. this test, for various reasons.
  14.  
  15. The bits of “flags” are tested independently. If flags&testClutQuicklyFlag then
  16. SetEntriesQuickly() will be tested instead of GDSetEntries/GDDirectSetEntries.
  17. If flags&testClutSeriallyFlag then the clut entries will be set individually,
  18. calling GDSetEntries once for each clut entry, to check the clut entry
  19. addressing. If flags&testClutLinearFlag then a simple sequence, will be loaded
  20. into the clut, instead of random numbers, to help figuring out systematic
  21. errors. The sequence is (0,0,0),(1,0,0), (0,2,0),(0,0,3),(4,4,4),(4,0,0), and so
  22. on.
  23.  
  24. Returned value is zero if ok, nonzero if error occurred.
  25.  
  26. GDTestClut recognizes the common driver errors and reports them in a sensible
  27. way, using the various fields of the card->depth[d].clut structure. Errors
  28. accumulate in the card->depth[d].clut structure, allowing you to make multiple
  29. calls to GDTestClut and only then summarize the results. It is important that
  30. you zero card->depth[d].clut.tests and card->depth[d].clutQuickly.tests before
  31. your first call to GDTestClut, to induce it to zero the rest of the clut
  32. structures.
  33.  
  34. Assumes that GDevice record is valid, i.e. the user has not called GDSetMode().
  35.  
  36. HISTORY:
  37. 3/9/93    dgp    code extracted from the demo TestCluts.c, to create a reusable
  38.         subroutine.
  39. 3/10/93    dgp    No longer assume that GDevice record reflects the actual state of the
  40.         driver.
  41. 4/5/93    dgp    Add support for grayB.
  42. 4/19/93    dgp    Fixed bug in VisibleHash that used garbage in place of
  43.             linear color table when gdType==directType. Use GDNewLinearColorTable.
  44. 5/11/93    dgp    GDTestClutHash now checks for valid device, in response to bug report
  45.             by Jonathan Brecher.
  46. 5/18/93    dgp    Allow small, 0.001, tolerance in establishing the identity transform.
  47. 5/25/93    dgp    Increased proportion of time spent loading clut in VisibleHash().
  48. 9/5/94 dgp removed assumption in printf's that int==short.
  49. 12/29/94 dgp WriteAndReadClut now waits for vbl before reading.
  50. 4/8/95 dgp I introduced WAIT_FOR_VBL to enable/disable waiting for vbl between 
  51.             write and read of CLUT. I introduce the wait to see if it would 
  52.             help certain video cards pass the tests. However, in both cases
  53.             it made no difference and the explanation turned out to be that
  54.             cscSetGamma wasn't supported. So, in the interest of fast testing,
  55.             I'm omitting the wait.
  56. */
  57. #include "VideoToolbox.h"
  58. //#include <Errors.h>
  59. #include "GDInfo.h"
  60. #define WAIT_FOR_VBL 0    // 0 for no wait; 1 to wait for vbl between write & read of CLUT.
  61.  
  62. // These functions are solely for use within this file.
  63. ColorSpec *MakeClutTable(GDHandle device,short flags);
  64. OSErr WriteClut(GDHandle device,ColorSpec putTable[],short flags);
  65. OSErr ShowGammaTable(FILE *o[2],GDHandle device);
  66. void RGBToGray(RGBColor *rgb,short dacSize);
  67. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask);
  68. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  69.     ,short *hashPtr);
  70. void GDRestoreBlackAndWhite(GDHandle device);
  71. OSErr EstimateClutTransform(short flags,VideoInfo *card);
  72. OSErr ComputeRMSClutError(FILE *file
  73.     ,ColorSpec *putTable,ColorSpec *getTable,short flags,VideoInfo *card);
  74. OSErr WriteAndReadClut(GDHandle device,short flags,ColorSpec **put,ColorSpec **get);
  75. #define SixteenBitGray 1
  76.  
  77. OSErr GDTestClut(FILE *o[2],short flags,VideoInfo *card)
  78. {
  79.     short i,j,clutSize,quickly,isGray;
  80.     int error;
  81.     ColorSpec *putTable=NULL,*getTable=NULL;
  82.     VideoCardClutTest *clut;
  83.     
  84.     if(card->device==NULL)return 0;
  85.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  86.     for(quickly=0;quickly<2;quickly++){
  87.         clut=&card->depth[card->d].clut[quickly][isGray];
  88.         if(!clut->read.tested){
  89.             for(j=0;j<3;j++)for(i=0;i<3;i++)clut->read.rgbGain[j][i]=NAN;
  90.             for(j=0;j<3;j++)clut->read.rgbError[j]=NAN;
  91.             for(j=0;j<3;j++)clut->read.rgbErrorAtOnce[j]=NAN;
  92.             clut->read.errors=0;
  93.             clut->read.errorsAtOnce=0;
  94.         }
  95.         if(!clut->visual.tested){
  96.             clut->visual.errors=0;
  97.             clut->visual.errorsAtOnce=0;
  98.         }
  99.         if(!clut->hash.tested){
  100.             clut->hash.errors=0;
  101.         }
  102.     }
  103.     quickly=((flags&testClutQuicklyFlag)!=0);
  104.     clut=&card->depth[card->d].clut[quickly][isGray];
  105.     if(!clut->read.doTest)return 0;
  106.     
  107.     GDInfo(card);
  108.     if(card->device==NULL)return 0;
  109.     clutSize=card->depth[card->d].clutSize;
  110.     error=GDSaveGamma(card->device);
  111.     error=GDUncorrectedGamma(card->device);
  112.     if(error)return error;
  113.     
  114.     error=EstimateClutTransform(flags&~testClutSeriallyFlag,card);
  115.     if(error)goto done;
  116.  
  117.     error=WriteAndReadClut(card->device,flags,&putTable,&getTable);
  118.     if(error)goto done;
  119.  
  120.     // COMPARE
  121.     ComputeRMSClutError(o[1],putTable,getTable,flags,card);
  122.     card->clutTested=1;
  123.     
  124.     done:
  125.     DisposePtr((Ptr)putTable);
  126.     DisposePtr((Ptr)getTable);
  127.     GDRestoreGamma(card->device);
  128.     GDRestoreDeviceClut(card->device);
  129.  
  130. //    clut->visual.doTest |= error && clut->hash.doTest;
  131. //    if(clut->visual.doTest)error=GDTestClutVisually(flags,card);
  132.     return error;
  133. }
  134.  
  135. OSErr EstimateClutTransform(short flags,VideoInfo *card)
  136. {
  137.     short i,j,tableSize,quickly,isGray;
  138.     int error=0;
  139.     ColorSpec *putTable,*getTable;
  140.     VideoCardClutTest *clut;
  141.     long putSum[3],getSum[3][3];
  142.     unsigned short *put3,*get3;
  143.         
  144.     error=WriteAndReadClut(card->device,flags|testClutGains,&putTable,&getTable);
  145.     if(error)return error;
  146.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  147.     
  148.     // Estimate transformation
  149.     for(i=0;i<3;i++){
  150.         putSum[i]=0;
  151.         for(j=0;j<3;j++)getSum[i][j]=0;
  152.     }
  153.     for(i=0;i<tableSize;i++){
  154.         put3=(unsigned short *)&putTable[i].rgb;
  155.         get3=(unsigned short *)&getTable[i].rgb;
  156.         putSum[i%3]+=put3[i%3];
  157.         for(j=0;j<3;j++)getSum[j][i%3]+=get3[j];
  158.     }
  159.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  160.     quickly=((flags&testClutQuicklyFlag)!=0);
  161.     clut=&card->depth[card->d].clut[quickly][isGray];
  162.     for(i=0;i<3;i++)for(j=0;j<3;j++){
  163.         clut->read.rgbGain[j][i]=(double)getSum[j][i]/putSum[i];
  164.         if(clut->read.rgbGain[j][i]!=floor(clut->read.rgbGain[j][i]))
  165.             clut->read.rgbGain[j][i]+=0.5*tableSize/putSum[i];
  166.     }
  167.     DisposePtr((Ptr)putTable);
  168.     DisposePtr((Ptr)getTable);
  169.     return error;
  170. }
  171.  
  172. OSErr WriteAndReadClut(GDHandle device,short flags,ColorSpec **put,ColorSpec **get)
  173. {
  174.     short i,clutSize,tableSize;
  175.     ColorSpec spec,*putTable,*getTable;
  176.     VBLTaskAndA5 vbl;
  177.     int error=0;
  178.  
  179.     clutSize=GDClutSize(device);
  180.  
  181.     // MAKE TABLE FOR CLUT
  182.     *put=putTable=MakeClutTable(device,flags);
  183.     if(putTable==NULL)return MemError();
  184.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  185.     
  186.     // MAKE BLANK TABLE
  187.     *get=getTable=(ColorSpec *)NewPtr(GetPtrSize((Ptr)putTable));
  188.     if(getTable==NULL)return MemError();
  189.     spec.value=spec.rgb.red=spec.rgb.green=spec.rgb.blue=0;
  190.     for(i=0;i<tableSize;i++)getTable[i]=spec;
  191.  
  192.     // WRITE CLUT & READ BACK, ONE CLUT-FULL AT A TIME
  193.     vbl.subroutine=NULL;                // request default subroutine
  194.     error=VBLInstall(&vbl,device,1000);
  195.     if(error)PrintfExit("%s line %d. WriteAndReadClut:VBLInstall error %d\n"
  196.         ,__FILE__,__LINE__,error);
  197.     vbl.vbl.vblCount=1;    // enable the interrupt service routine
  198.     for(i=0;i<tableSize;i+=clutSize){
  199.         error=WriteClut(device,&putTable[i],flags);
  200.         if(error)break;
  201.         vbl.newFrame=0;
  202.         if(WAIT_FOR_VBL)while(vbl.newFrame==0) ;    // wait until clut's been loaded before reading it
  203.         error=GDGetEntries(device,0,clutSize-1,&getTable[i]);
  204.         if(error)break;
  205.     }
  206.     VBLRemove(&vbl);
  207.     return error;
  208. }
  209.  
  210. OSErr ComputeRMSClutError(FILE *file
  211.     ,ColorSpec *putTable,ColorSpec *getTable,short flags,VideoInfo *card)
  212. {
  213.     short i,j,k,clutSize,tableSize,quickly,isGray,integralGains;
  214.     unsigned short *put3,*get3,n[3],bad;
  215.     double e,squaredError[3],model[3];
  216.     VideoCardClutTest *clut;
  217.     int error=0;
  218.         
  219.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  220.     quickly=((flags&testClutQuicklyFlag)!=0);
  221.     clut=&card->depth[card->d].clut[quickly][isGray];
  222.     clutSize=GDClutSize(card->device);
  223.     tableSize=GetPtrSize((Ptr)putTable)/sizeof(putTable[0]);
  224.  
  225.     // Estimate error of linear model
  226.     for(i=0;i<3;i++){
  227.         squaredError[i]=0;
  228.         n[i]=0;
  229.     }
  230.     bad=0;
  231.     // Allow error of a few least significant steps of dac
  232.     clut->read.tolerance=2.9*(1<<(16-GDDacSize(card->device)));
  233.     for(i=0;i<tableSize;i++){
  234.         put3=(unsigned short *)&putTable[i].rgb;
  235.         get3=(unsigned short *)&getTable[i].rgb;
  236.         for(j=0;j<3;j++){
  237.             model[j]=put3[0]*clut->read.rgbGain[j][0]
  238.                 +put3[1]*clut->read.rgbGain[j][1]
  239.                 +put3[2]*clut->read.rgbGain[j][2];
  240.             e=get3[j]-model[j];
  241.             bad|=fabs(e)>clut->read.tolerance;
  242.             squaredError[j]+=e*e;
  243.             n[j]++;
  244.         }
  245.     }
  246.     for(i=0;i<3;i++){
  247.         clut->read.rgbError[i]=sqrt(squaredError[i]/n[i]);
  248.         if(!(flags&testClutSeriallyFlag))clut->read.rgbErrorAtOnce[i]=sqrt(squaredError[i]/n[i]);
  249.     }
  250.  
  251.     clut->read.identity=1;
  252.     for(i=0;i<3;i++)for(j=0;j<3;j++)clut->read.identity&=(fabs(clut->read.rgbGain[i][j]-(i==j))<0.001);
  253.     
  254.     if(bad){
  255.         clut->read.errors=1;
  256.         if(!(flags&testClutSeriallyFlag))clut->read.errorsAtOnce=1;
  257.     }
  258.     if(bad){
  259.         // Print one-line error message to file.
  260.         fprintf(file,"\n");
  261.         if(flags&testClutQuicklyFlag)fprintf(file,"SetEntriesQuickly != cscGetEntries. ");
  262.         else switch((*card->device)->gdType){
  263.         case fixedType:
  264.             break;
  265.         case clutType:
  266.             fprintf(file,"cscSetEntries != cscGetEntries. ");
  267.             break;
  268.         case directType:
  269.             fprintf(file,"cscDirectSetEntries != cscGetEntries. ");
  270.             break;
  271.         }
  272.         fprintf(file,"%d-bit ",(int)card->depth[card->d].pixelSize);
  273.         if(isGray)fprintf(file,"gray pixels.");
  274.         else fprintf(file,"color pixels.");
  275.         if(flags&testClutSeriallyFlag)fprintf(file," Loaded one clut entry at a time.\n");
  276.         else fprintf(file," Loaded whole clut at once.\n");
  277.  
  278.         // Print the estimated color transformation matrix
  279.         fprintf(file,"\"expected\" assumes the best-fit linear color transformation matrix:\n");
  280.         integralGains=1;
  281.         for(j=0;j<3;j++)for(k=0;k<3;k++)
  282.                 integralGains&=(clut->read.rgbGain[j][k]==floor(clut->read.rgbGain[j][k]));
  283.         for(j=0;j<3;j++){
  284.             fprintf(file," (%cOut±%4.1f%%)%c"
  285.                 ,"RGB"[j],clut->read.rgbError[j]*100./USHRT_MAX," = "[j]);
  286.             if(integralGains)fprintf(file,"(%1.0f %1.0f %1.0f)"
  287.                 ,clut->read.rgbGain[j][0],clut->read.rgbGain[j][1],clut->read.rgbGain[j][2]);
  288.             else fprintf(file,"(%3.2f %3.2f %3.2f)"
  289.                 ,clut->read.rgbGain[j][0],clut->read.rgbGain[j][1],clut->read.rgbGain[j][2]);
  290.             fprintf(file,"%c(%cIn)\n"," x "[j],"RGB"[j]);
  291.         }
  292.         
  293.         // Print each clut error.
  294.         for(i=0;i<tableSize;i++){
  295.             put3=(unsigned short *)&putTable[i].rgb;
  296.             get3=(unsigned short *)&getTable[i].rgb;
  297.             bad=0;
  298.             for(j=0;j<3;j++){
  299.                 model[j]=put3[0]*clut->read.rgbGain[j][0]
  300.                     +put3[1]*clut->read.rgbGain[j][1]
  301.                     +put3[2]*clut->read.rgbGain[j][2];
  302.                 e=get3[j]-model[j];
  303.                 bad|=fabs(e)>clut->read.tolerance;
  304.             }
  305.             if(0 && bad)fprintf(file,"Clut[%3d] wrote(%04u,%04u,%04u) "
  306.                 "expected(%04.0f,%04.0f,%04.0f) but read(%04u,%04u,%04u)\n"
  307.                 ,i%clutSize,put3[0],put3[1],put3[2]
  308.                 ,model[0],model[1],model[2]
  309.                 ,get3[0],get3[1],get3[2]);
  310.             if(bad)fprintf(file,"Clut[%3d] wrote(%04x,%04x,%04x) "
  311.                 "expected(%04x,%04x,%04x) but read(%04x,%04x,%04x)\n"
  312.                 ,i%clutSize,put3[0],put3[1],put3[2]
  313.                 ,(unsigned short)(0.5+model[0]),(unsigned short)(0.5+model[1]),(unsigned short)(0.5+model[2])
  314.                 ,get3[0],get3[1],get3[2]);
  315.         }
  316.         fprintf(file,"\n");
  317.     }
  318.     clut->read.tested=1;
  319.     return error;
  320. }
  321.  
  322. OSErr GDTestClutVisually(short flags,VideoInfo *card)
  323. {
  324.     int error;
  325.     short clutSize,quickly,isGray;
  326.     ColorSpec *putTable,*normalTable;
  327.     Boolean weirdError,normalError;
  328.     VideoCardClutTest *clut;
  329.     
  330.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  331.     quickly=((flags&testClutQuicklyFlag)!=0);
  332.     clut=&card->depth[card->d].clut[quickly][isGray];
  333.     if(clut->visual.tested==0){
  334.         clut->visual.errors=0;
  335.         clut->visual.errorsAtOnce=0;
  336.     }
  337.     if(card->device==NULL)return 0;
  338.     clutSize=GDClutSize(card->device);
  339.     normalTable=((**(**(**card->device).gdPMap).pmTable)).ctTable;
  340.     if(card->device==GetMainDevice())
  341.         putTable=MakeClutTable(card->device,testClutNegativeFlag);
  342.     else putTable=MakeClutTable(card->device,flags);
  343.     if(putTable==NULL)return MemError();
  344.     GDSaveGamma(card->device);
  345.     GDUncorrectedGamma(card->device);
  346.     error=WriteClut(card->device,putTable,flags);
  347.     if(error){
  348.         GDRestoreGamma(card->device);
  349.         GDRestoreDeviceClut(card->device);
  350.         return error;
  351.     }
  352.     printf(BLANKLINE);
  353.     printf("Screen should be weirdly colored; watch for subtle change as you hit return:" "\r");
  354.     while(getcharUnbuffered()==-1) ;
  355.     error=WriteClut(card->device,putTable,0);
  356.     if(error){
  357.         GDRestoreGamma(card->device);
  358.         GDRestoreDeviceClut(card->device);
  359.         return error;
  360.     }
  361.     printf(BLANKLINE);
  362.     printf("Did you see any change at all?");
  363.     weirdError=YesOrNo(0);
  364.     printf("\r");
  365.     error=WriteClut(card->device,normalTable,flags);
  366.     printf(BLANKLINE);
  367.     printf("Screen should be normal now; watch for subtle change as you hit return:" "\r");
  368.     while(getcharUnbuffered()==-1) ;
  369.     error=WriteClut(card->device,normalTable,0);
  370.     printf(BLANKLINE);
  371.     printf("Did you see any change at all?");
  372.     normalError=YesOrNo(0);
  373.     printf("\r");
  374.     GDRestoreGamma(card->device);
  375.     GDRestoreDeviceClut(card->device);
  376.     DisposePtr((Ptr)putTable);
  377.     clut->visual.tested++;
  378.     clut->visual.errors+=(weirdError || normalError);
  379.     if(!(flags&testClutSeriallyFlag))clut->visual.errorsAtOnce+=(weirdError || normalError);
  380.     return 0;
  381. }
  382.  
  383. ColorSpec *MakeClutTable(GDHandle device,short flags)
  384. {
  385.     short i,j,clutSize;
  386.     RGBColor put;
  387.     ColorSpec *table;
  388.     
  389.     clutSize=GDClutSize(device);
  390.     if((flags&testClutGains) && clutSize<16)clutSize=16;
  391.     table=(ColorSpec *)NewPtr(sizeof(*table)*clutSize);
  392.     if(table==NULL)return table;
  393.     for(i=0;i<clutSize;i++) {
  394.         if(flags&testClutNegativeFlag){
  395.             put=((**(**(**device).gdPMap).pmTable)).ctTable[clutSize-1-i].rgb;
  396.         }else if(flags&testClutGains){
  397.             // Estimate rgb gains of any transformation
  398.             put.red=put.green=put.blue=0;
  399.             j=(0xffffL*i+(clutSize-1)/2)/(clutSize-1);
  400.             switch(i%3){
  401.             case 0:
  402.                 put.red=j;
  403.                 break;
  404.             case 1:
  405.                 put.green=j;
  406.                 break;
  407.             case 2:
  408.                 put.blue=j;
  409.                 break;
  410.             }
  411.         }else if(flags&testClutLinearFlag){
  412.             // Linear test pattern
  413.             put.red=put.green=put.blue=0;
  414.             j=0xffffffff*(i+(clutSize-1)/2)/(clutSize-1);
  415.             switch(i%4){
  416.             case 0:
  417.                 put.red=put.green=put.blue=j;
  418.                 break;
  419.             case 1:
  420.                 put.red=j;
  421.                 break;
  422.             case 2:
  423.                 put.green=j;
  424.                 break;
  425.             case 3:
  426.                 put.blue=j;
  427.                 break;
  428.             }
  429.         }else{
  430.             // Random test pattern
  431.             put.red=randU();
  432.             put.green=randU();
  433.             put.blue=randU();
  434.         }
  435.         if(!SixteenBitGray){
  436.             put.red&=0xff00;
  437.             put.green&=0xff00;
  438.             put.blue&=0xff00;
  439.         }
  440.         table[i].rgb=put;
  441.     }
  442.     return table;
  443. }
  444.  
  445. OSErr WriteClut(GDHandle device,ColorSpec putTable[],short flags)
  446. {
  447.     short i,clutSize=GDClutSize(device);
  448.     char priority=7;
  449.     int error;
  450.     SetEntriesFunction function;
  451.         
  452.     if(flags&testClutQuicklyFlag)function=SetEntriesQuickly;
  453.     else function=GDSetEntriesByType;
  454.     if(flags&testClutSeriallyFlag){
  455.         // Load one clut entry at a time
  456.         for(i=0;i<clutSize;i++){
  457.             SwapPriority(&priority);    // Force driver to load clut now.
  458.             error=(function)(device,i,0,&putTable[i]);
  459.             SwapPriority(&priority);
  460.             if(error)return error;
  461.         }
  462.     }else{
  463.         // Load whole clut at once
  464.         SwapPriority(&priority);    // Force driver to load clut now.
  465.         error=(function)(device,0,clutSize-1,putTable);
  466.         SwapPriority(&priority);
  467.         if(error)return error;
  468.     }
  469.     return 0;
  470. }
  471.  
  472. OSErr GDTestClutHash(short flags,VideoInfo *card){
  473.     SetEntriesFunction function;
  474.     int error;
  475.     short isGray,quickly;
  476.     VideoCardClutTest *clut;
  477.     
  478.     if(card->device==NULL)return 0;
  479.     quickly=((flags&testClutQuicklyFlag)!=0);
  480.     isGray=!TestDeviceAttribute(card->device,gdDevType);
  481.     clut=&card->depth[card->d].clut[quickly][isGray];
  482.     if(clut->hash.tested==0)clut->hash.errors=0;
  483.     if(!clut->hash.doTest)return 0;
  484.     if(quickly)function=SetEntriesQuickly;
  485.     else function=GDSetEntriesByType;
  486.     error=VisibleHash(card->device,function,0,&clut->hash.errors);
  487.     clut->hash.tested=1;
  488.     return error;
  489. }
  490.  
  491. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  492.     ,short *hashPtr)
  493. {
  494.     int error;
  495.     short clutSize;
  496.     short hash;
  497.     long tick;
  498.     ColorSpec *putTable,*linearTable=NULL;
  499.  
  500.     if(device==NULL || (**device).gdType==fixedType)return 0;
  501.     clutSize=GDClutSize(device);
  502.     if(clutEntries<0 || clutEntries>clutSize)return 1;
  503.     if(clutEntries==0)clutEntries=clutSize;
  504.     if((**device).gdType==directType){
  505.         if(function==GDSetEntries)function=GDDirectSetEntries;
  506.         putTable=linearTable=GDNewLinearColorTable(device);
  507.         if(linearTable==NULL)return MemError();
  508.     }else putTable=((**(**(**device).gdPMap).pmTable)).ctTable;
  509.     error=(function)(device,0,clutEntries-1,putTable);
  510.     if(!error){
  511.         printf(BLANKLINE);
  512.         if(device==GetMainDevice())
  513.             printf("Do you see any dynamic black specks on this screen? (No):");
  514.         else printf("Do you see any dynamic black specks on the test screen? (No):");
  515.         fflush(stdout);
  516.         do{
  517.             tick=TickCount();
  518.             do{
  519.                 (function)(device,0,clutEntries-1,putTable);
  520.             }while(TickCount()-tick<30);
  521.         }while(!kbhit());
  522.         hash=YesOrNo(0);
  523.         printf("\r");
  524.         if(linearTable!=NULL)DisposePtr((Ptr)linearTable);
  525.         if(hashPtr!=NULL)*hashPtr=hash;
  526.     }
  527.     return error;
  528. }
  529.  
  530. void GDRestoreBlackAndWhite(GDHandle device)
  531. // Restore the first & last clut entries to white and black.
  532. {
  533.     short clutSize;
  534.     int error;
  535.     ColorSpec white={255,0xffff,0xffff,0xffff},black={0,0,0,0};
  536.  
  537.     clutSize=GDClutSize(device);
  538.     switch((**device).gdType){
  539.     case clutType:
  540.         error=GDSetEntries(device,0,0,&white);
  541.         error=GDSetEntries(device,clutSize-1,0,&black-(clutSize-1));
  542.         break;
  543.     case directType:
  544.         error=GDDirectSetEntries(device,0,0,&black);
  545.         error=GDDirectSetEntries(device,clutSize-1,0,&white-(clutSize-1));
  546.         break;
  547.     default:
  548.         break;
  549.     }
  550. }
  551. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask)
  552. {
  553.     return (a->red&mask)!=(b->red&mask)
  554.         ||(a->green&mask)!=(b->green&mask)
  555.         ||(a->blue&mask)!=(b->blue&mask);
  556. }
  557.  
  558. /*
  559. When you set a video screen to monochrome or "gray" (as opposed to "color"),
  560. e.g. using the Control Panel:Monitors, the request is passed on to the video
  561. driver. The video driver transforms each of your rgb triplets to a
  562. luminance-equivalent gray, using a formula that must be very similar, if not
  563. equivalent, to the code below. The rounding is bad, e.g. any gray rgb triplet
  564. (i,i,i), other than (0,0,0), is transformed to (i-1,i-1,i-1), which is darker,
  565. failing to preserve luminance. However, my goal was to replicate Apple's crumby
  566. transformation, not to improve it. I presume that the reason that I have to trim
  567. my numbers down a tad (-0.00001) is that I'm doing this with 80-bit precision
  568. whereas the driver uses the 64-bit precision of the SANE routines. Presumably I
  569. could obtain the same result by compiling this subroutine separately, to use
  570. 64-bit floating point, since all the arguments are ints.
  571. */
  572. void RGBToGray(RGBColor *rgb,short dacSize)
  573. // Empirical formula to replicate Apple's luminance mapping.
  574. {
  575.     short i,shift;
  576.     unsigned long n;
  577.     
  578.     if(!SixteenBitGray){
  579.         shift=16-dacSize;
  580.         i=(rgb->red>>shift)*(0.30-0.00001)
  581.             +(rgb->green>>shift)*(0.59-0.00001)+(rgb->blue>>shift)*(0.11);
  582.         n=0xffffffffUL/((1<<dacSize)-1);
  583.         rgb->red=rgb->green=rgb->blue=(i*n)>>16;
  584.     }else{
  585.         rgb->red=rgb->green=rgb->blue=rgb->red*(0.30-0.00001)
  586.             +rgb->green*(0.59)+rgb->blue*(0.11-0.00001);
  587.     }
  588. }
  589.  
  590.